
#include <iostream>
using namespace std;

// // DEBUG:  
// #include <fstream>

#include <qmessagebox.h>

#include "PacketSniffer.h"



PacketSniffer::PacketSniffer( QObject* parent,
			      const char* name,
			      const FilterGroup& filterGroup,
			      const QString& filter,
			      const bool& tcpOnly )
    : 
    QObject( parent, name ),
    initSuccess_( false ),
    filter_( filter ),
    filterGroup_( filterGroup ),
    tcpOnly_( tcpOnly )
{
    // Set the filter according to filter group.
    if( filterGroup_ == None ) 
    {
	filter_ = "";
    }
    else if( filterGroup_ == Bnet )
    {
	filter_ = "tcp port 6112";
	tcpOnly_ = true;
    }
    else if( filterGroup_ == Custom )
    {
	filter_ = filter;
    }
    
    // Get the device list.
    pcap_if_t *d;
    char errbuf[PCAP_ERRBUF_SIZE];
    
    // Retrieve the device list.
    int rc = pcap_findalldevs( &alldevs_, errbuf );
    if( rc == -1 )
    {
	initSuccess_ = false;
	QMessageBox::critical( 0, tr("PacketSniffer Initialization Failed"),
			       QString(tr("Error message:  ")) + 
			       QString( errbuf ) );
	return;
    }

    
    // Create a worker thread for each device.
    for( d = alldevs_; 
	 d != 0; 
	 d = d->next )
    {
	PacketSnifferWorkerThread* workerThread = 
	    new PacketSnifferWorkerThread( &waitForPacket_, d,
					       100, 1, filter_, tcpOnly_ );
	
	// If the worker thread succeeded in initializing the device,
	// then keep it, otherwise free the memory used for it.
	if( workerThread->initSuccess() == true )
	{
	    workerThreads_.push_back( workerThread );
	}
	else
	{
	    delete workerThread;
	}
    }  // End of  FOR loop over devices.

    initSuccess_ = true;
}

PacketSniffer::~PacketSniffer()
{
    // Deallocate memory used for each of the worker threads.
    for( QValueList<PacketSnifferWorkerThread*>::iterator iter = 
	     workerThreads_.begin();
	 iter != workerThreads_.end();
	 iter++ )
    {
	// According to the Qt docs, it's bad to terminate the thread
	// like this because it could catch the tread mid-process into
	// doing something with memory.  This is true, but the
	// algorithm we use right now, only does 1 packet at a time,
	// and this is at exit time (since only 1 PacketSniffer
	// per computer/app).  Hopefully the OS will clean up whatever
	// we don't catch nicely.
	(*iter)->terminate();

	// Synchronously waiting for the thread to die before deleting.
	//(*iter)->wait();

	// Deallocate mem.
	delete (*iter);
    }  // End of  FOR loop over the worker threads.
    workerThreads_.clear();

    // Free the pcap alldevs_ thing we made in the constructor.
    pcap_freealldevs(alldevs_);
}

bool PacketSniffer::initSuccess() const
{
    return initSuccess_;
}


// Sniffs the flow-data of the TCP packet if the filtergroupp is Bnet.
// Otherwise it only sniffs the actual IP packets.
QValueList<QByteArray> PacketSniffer::sniff()
{
    // If Init failed, then just return an empty list of QByteArrays.
    if( !initSuccess_ )
    {
	return QValueList<QByteArray>();
    }

//     ofstream debugsniffer;
//     debugsniffer.open("debugsniffer.txt", ios::out | ios::app);
//     if( !debugsniffer.is_open() )
//     {
// 	return 2;
//     }

    
    byteArrays_.clear();
    
    // Now that we have that out of the way...we don't leave untill we
    // have at least 1 QByteArray.
    while( byteArrays_.empty() == true ) 
    {
	// Do a check on each thread.
	for( QValueList<PacketSnifferWorkerThread*>::iterator iter = 
		 workerThreads_.begin();
	     iter != workerThreads_.end();
	     iter++ )
	{
	    // If it has a packet ready, grab it.
	    if( (*iter)->packetReady() )
	    {
		QByteArray byteArray = (*iter)->getByteArray();
		byteArrays_.push_back( byteArray.copy() );
		emit sniffed( byteArray );
// 		debugsniffer << "push-ed back one" << endl;
// 		debugsniffer.flush();
	    }

	    // If it's not running, but it initialized okay, then
	    // start the thread.
	    if( !(*iter)->running() && (*iter)->initSuccess() )
	    {
// 		debugsniffer << "starting a thread up" << endl;
// 		debugsniffer.flush();
		(*iter)->start();
	    }
	}

	if( byteArrays_.empty() ) 
	{
	    // Wait for a thread to finish gathering a packet.
	    waitForPacket_.wait( 200 );  // 200 Millisecond timeout.
	}
// 	debugsniffer << "loop iteration" << endl;
// 	debugsniffer.flush();
    }

    // Create the return value, and clear byteArrays_.
    QValueList<QByteArray> rv = byteArrays_;
    byteArrays_.clear();


//     debugsniffer << "leaving sniff()\n";
//     debugsniffer.close();

    return rv;
}

